package cn.edu.hitwh.kanlema.service.impl;

import cn.edu.hitwh.kanlema.constant.MqConstants;
import cn.edu.hitwh.kanlema.constant.RedisConstants;
import cn.edu.hitwh.kanlema.constant.SystemConstants;
import cn.edu.hitwh.kanlema.dto.ScrollResult;
import cn.edu.hitwh.kanlema.dto.UserDTO;
import cn.edu.hitwh.kanlema.entity.Blog;
import cn.edu.hitwh.kanlema.entity.User;
import cn.edu.hitwh.kanlema.exception.ApiException;
import cn.edu.hitwh.kanlema.mapper.BlogMapper;
import cn.edu.hitwh.kanlema.message.FeedMessage;
import cn.edu.hitwh.kanlema.service.IBlogService;
import cn.edu.hitwh.kanlema.service.IUserService;
import cn.edu.hitwh.kanlema.util.UserHolder;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author vvcoder
 * @since 2025-8-18
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
    @Resource
    private IUserService userService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Snowflake snowflake;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public List<Blog> getHotPage(Integer current) {
        // 根据用户查询
        Page<Blog> page = lambdaQuery()
                .orderByDesc(Blog::getLiked)
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        // 查询用户
        records.forEach(blog -> {
            Long userId = blog.getUserId();
            User user = userService.getById(userId);
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
        });
        return records;
    }

    @Override
    public Blog getBlogById(Long id) {
        Blog blog = getById(id);
        if (blog == null) {
            throw new ApiException("笔记不存在");
        }
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
        // 检查用户是否点赞过
        blog.setIsLike(redisTemplate.opsForZSet()
                .score(RedisConstants.BLOG_LIKED_KEY + id, userId.toString()) != null);
        return blog;
    }

    @Override
    public void likeBlog(Long id) {
        Long userId = UserHolder.getUser().getId();

        // 检查笔记是否存在
        Blog blog = getById(id);
        if (blog == null) {
            throw new ApiException("笔记不存在");
        }

        // 检查用户是否点赞过
        if (Boolean.TRUE.equals(redisTemplate.opsForZSet()
                .add(RedisConstants.BLOG_LIKED_KEY + id, userId.toString(), System.currentTimeMillis()))) {
            // 点赞数量增加
            baseMapper.addLike(id, 1L);
        } else if (!Objects.equals(0L, redisTemplate.opsForZSet()
                .remove(RedisConstants.BLOG_LIKED_KEY + id, userId.toString()))) {
            // 点赞数量减少
            baseMapper.addLike(id, -1L);
        } else {
            throw new ApiException("操作过于频繁");
        }
    }

    @Override
    public List<UserDTO> getBlogLikes(Long id) {
        final long top = 5;
        // 查询点赞用户
        Set<String> range = redisTemplate.opsForZSet()
                .range(RedisConstants.BLOG_LIKED_KEY + id, 0, top - 1);
        if (CollUtil.isEmpty(range)) {
            return Collections.emptyList();
        }
        List<Long> userIds = range.stream()
                .map(Long::valueOf)
                .collect(Collectors.toList());
        // 查询用户
        List<User> users = userService.listByIds(userIds);
        Map<Long, UserDTO> userMap = users.stream()
                .collect(Collectors.toMap(
                        User::getId,
                        user -> BeanUtil.copyProperties(user, UserDTO.class)
                ));
        // 恢复顺序
        return userIds.stream()
                .map(userMap::get)
                .collect(Collectors.toList());
    }

    @Override
    public Long saveBlog(Blog blog) {
        Long userId = UserHolder.getUser().getId();
        // 生成主键
        blog.setId(snowflake.nextId());
        blog.setUserId(userId);
        baseMapper.insert(blog);

        // 推流
        FeedMessage feedMessage = new FeedMessage();
        feedMessage.setBlogId(blog.getId());
        feedMessage.setUserId(userId);
        rabbitTemplate.convertAndSend(MqConstants.BLOG_EXCHANGE, MqConstants.BLOG_SEND_KEY, feedMessage);

        return blog.getId();
    }

    @Override
    public ScrollResult queryBlogOfFollow(Long lastId) {
        Long userId = UserHolder.getUser().getId();
        final long count = 5;
        long offset = 1;
        if (lastId == null) {
            lastId = Long.MAX_VALUE;
            offset = 0;
        }
        // 从Redis中查询推流
        String feedKey = RedisConstants.BLOG_FEED_KEY + userId;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(feedKey, 0, lastId, offset, count);

        List<Blog> blogs;
        long minTime;
        if (CollUtil.isNotEmpty(typedTuples)) {
            List<Long> ids = new ArrayList<>();
            minTime = Long.MAX_VALUE;
            for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {
                ids.add(Long.valueOf(tuple.getValue()));
                minTime = Math.min(minTime, tuple.getScore().longValue());
            }
            // 查询博客
            blogs = baseMapper.selectBatchIds(ids);
        } else {
            blogs = new ArrayList<>((int) count);
            minTime = Long.MAX_VALUE;
        }

        if (CollUtil.isEmpty(blogs) || count > blogs.size()) {
            blogs.addAll(
                    baseMapper.getBlogOfFollow(userId, lastId, count - blogs.size())
            );
            if (blogs.isEmpty()) {
                ScrollResult result = new ScrollResult();
                result.setList(blogs);
                return result;
            }
            minTime = blogs.stream()
                    .map(Blog::getId)
                    .min(Long::compareTo)
                    .orElse(Long.MAX_VALUE);
        }

        ScrollResult result = new ScrollResult();
        // 查询用户
        Map<Long, UserDTO> userMap = userService.listByIds(
                        blogs.stream()
                                .map(Blog::getUserId)
                                .collect(Collectors.toSet())
                )
                .stream()
                .collect(Collectors.toMap(
                        User::getId,
                        user -> BeanUtil.copyProperties(user, UserDTO.class)
                ));
        // 填充数据
        blogs.forEach(blog -> {
            UserDTO user = userMap.get(blog.getUserId());
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
        });
        result.setList(blogs);
        result.setMinTime(minTime);
        result.setOffset(0);

        return result;
    }
}
